﻿namespace BlogsAPI.Sites
{
    using System;
    using System.IO;
    using System.Collections.Generic;
    using System.Text;
    using System.Net;
    using System.Web;
    using System.Text.RegularExpressions;
    using System.Security.Cryptography;
    using Abstract;
    using Interfaces;
    using System.Diagnostics;
    using System.Threading;

    /// <summary>
    /// Блогплатформа сделанная на базе движка живого журнала
    /// </summary>
    /// <seealso cref="http://lj.rossia.org"/>
    [BlogServiceAPI(Description = "Тифаретник", Link = "http://lj.rossia.org")]
    public class LjRossia : BlogServer
    {
        private static readonly Regex Validation = new Regex(@"^http://lj\.rossia\.org", Regexoptions);
        private static readonly Regex ValidationFull = new Regex(@"^http://lj\.rossia\.org/(users|community)/([^/]+)(?:/|$)", Regexoptions);
        
        private static readonly Regex eljFormAuth = new Regex(@"name.{2}lj_form_auth\W[^>]*?value\W{2}([^'""]*)\W", Regexoptions);

        /// <summary>
        /// Извлечение идентификационного параметра формы
        /// </summary>
        /// <param name="page">страница сайта</param>
        /// <returns></returns>
        private string GetFormAuth(string page)
        {
            //lj_form_auth
            Match m = eljFormAuth.Match(page);
            if (!m.Success)
            {
                throw new Exception("Не найден номер lj_form_auth");
            }
            return m.Groups[1].ToString();
        }

        #region interface: BlogServer

        [DebuggerStepThrough]
        public LjRossia()
        {
            Encoding = Encoding.UTF8;
        }

        /// <summary>
        /// проверка, является ли передаваемый урл чатью этого сервиса
        /// </summary>
        public override Boolean ValidateLink(String sLink)
        {
            return Validation.Match(sLink).Success;
        }

        /// <summary>
        /// проверка, является ли передаваемый урл чатью этого сервиса
        /// </summary>
        /// <param name="sLink">ссылка для проверки</param>
        /// <param name="userName">Имя пользователя на блогохостинге</param>
        /// <param name="bType">Тип блога - пользователь или сообщество</param>
        public override Boolean ValidateLink(String sLink, out String userName,out BlogType bType)
        {
            bType = BlogType.Unknown;
            Match m = ValidationFull.Match(sLink);
            if (m.Success)
            {
                //http://lj.rossia.org/users/tiphareth/
                bType = String.Compare("community",m.Groups[1].Value,true)==0?BlogType.Community:BlogType.User;
                userName = m.Groups[2].ToString();
                userName = userName.Replace("-", "_");
            }
            else
            {
                userName = String.Empty;
            }
            return m.Success;
        }

        /// <summary>
        /// Получение ссылки на блог блогера
        /// </summary>
        /// <param name="userName">ник блогера</param>
        /// <param name="btype">Тип блога - пользователь или сообщество</param>
        public override String GetBlogLink(string userName, BlogType btype)
        {
            return String.Format("http://lj.rossia.org/{1}/{0}", userName, btype == BlogType.Community?"community":"users");
        }

        /// <summary>
        /// Формирование HTML кода для отображения имени пользователя
        /// </summary>
        /// <param name="userName">ник блогера</param>
        /// <param name="bt">Тип блога - пользователь или сообщество</param>
        /// <returns>HTML</returns>
        public override String GetUsernameHtml(string userName, BlogType bt)
        {
            return String.Format("<span class='ljuser' lj:user='{0}' style='white-space: nowrap;'>"+
                "<a href='{1}'><img src='//blob.t30p.ru/img/userinfo.gif' alt='{0}' width='17' height='17' style='vertical-align: bottom; border: 0;' /></a>" +
                "<a href=\"{2}\"><b>{0}</b></a></span>",
                userName,"http://lj.rossia.org/userinfo.bml?user="+userName,
                GetBlogLink(userName, bt));
        }

        #region Функционал экспорта данных

        /// <summary>
        /// Получение ссылки на RSS блогера
        /// </summary>
        /// <param name="userName">ник блогера</param>
        /// <param name="btype">Тип блога - сообщество или блог</param>
        /// <remarks>Неработает если имя пользователя русское, тогда нужен номер</remarks>
        public override String GetRssLink(string userName, BlogType btype)
        {
            return GetBlogLink(userName, btype) + "/data/rss";
        }

        /// <summary>
        /// Получение ссылки на FOAF блогера
        /// </summary>
        /// <param name="userName">ник блогера</param>
        /// <param name="btype">Тип блога - сообщество или блог</param>
        /// <remarks>Неработает если имя пользователя русское, тогда нужен номер</remarks>
        public override String GetFoafLink(string userName, BlogType btype)
        {
            return GetBlogLink(userName, btype) + "/data/foaf";
        }

        #endregion


        /// <summary> 
        /// Реализует $response = md5($chal.md5($pass));
        /// </summary>
        /// <param name="pwd"></param>
        /// <param name="chal"></param>
        /// <returns></returns>
        private string MakePwdMd5(string pwd, string chal)
        {
            return GetMd5Hash(chal + GetMd5Hash(pwd));
        }

        private string GetMd5Hash(string input)
        {
            MD5CryptoServiceProvider x = new MD5CryptoServiceProvider();
            byte[] bs = Encoding.GetBytes(input);
            bs = x.ComputeHash(bs);
            StringBuilder s = new StringBuilder();
            foreach (byte b in bs)
            {
                s.Append(b.ToString("x2").ToLower());
            }
            return s.ToString();
        }

        /// <summary>
        /// Залогинивание пользователя в ЖЖ
        /// Однако для постинга-комментинга она не используется
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public override bool LogInto(UserAccount user)
        {
            if (_isLoggedIn) return true;
            HttpWebRequest myHttpWebRequest = MakeRequest("http://lj.rossia.org/login.bml?nojs=1");
            myHttpWebRequest.Method = "GET";
            myHttpWebRequest.Referer = "http://lj.rossia.org";

            HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
            //получаем ответ
            string result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding).ReadToEnd();
            myHttpWebResponse.Close();
            //chal
            Regex eChal = new Regex(@"name.{2}chal\W[^>]*?value\W{2}([^'""]*)\W", Regexoptions);
            Match m = eChal.Match(result);
            if (!m.Success)
            {
                Log("Попытка залогинивания не удалась, не найден номер CHAL");
                return false;
            }

            myHttpWebRequest = MakeRequest("http://lj.rossia.org/login.bml?nojs=1");
            myHttpWebRequest.Method = "POST";
            myHttpWebRequest.Referer = "http://lj.rossia.org/login.bml?nojs=1";
            myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
            ///////////////////////////
            myHttpWebRequest.AllowAutoRedirect = false;
            // передаем параметры
            string sQueryString = String.Format("response={0}&chal={1}&lj_form_auth={2}&user={3}&password=",
                MakePwdMd5(user.password, m.Groups[1].ToString()),
                HttpUtility.UrlEncode(m.Groups[1].ToString()),
                HttpUtility.UrlEncode(_userId),
                user.username
                );
            //***************//
            byte[] byteArr = Encoding.GetBytes(sQueryString);
            myHttpWebRequest.ContentLength = byteArr.Length;
            myHttpWebRequest.GetRequestStream().Write(byteArr, 0, byteArr.Length);
            myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
            //получаем ответ
#if DEBUG
            result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding).ReadToEnd();
#endif
            myHttpWebResponse.Close();
            //должны были получить ответ с нужными куками
            //sCookie =
            //   new Regex(@"//[^;]+;", Regexoptions).Replace(myHttpWebResponse.Headers[HttpResponseHeader.SetCookie],";");
            CookieParse(myHttpWebResponse.Headers[HttpResponseHeader.SetCookie]);
            //стираем куку привязки к определенному домену.
            sCookie = new Regex(@"ljdomsess\.[^;]+(?:;|$)", Regexoptions).Replace(sCookie, "");
            Log("LogInto " + user + " gains cookie = " + sCookie);
            ///
            _isLoggedIn = sCookie.Contains("ljsession");
            if (_isLoggedIn)
            {
                LoggedInAs = user;
                Log("Успешно залогинились как пользователь с lj_form_auth номером " + _userId);
            }
            return _isLoggedIn;
        }

        #endregion
    }
}


